package com.tang.portal.facade.admin.sys;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.tang.framework.core.domain.HttpResult;
import com.tang.framework.core.facade.Facade;
import com.tang.framework.core.model.LoginUser;
import com.tang.common.model.req.ReqId;
import com.tang.common.constant.Constant;
import com.tang.common.model.req.ReqIds;
import com.tang.service.admin.domain.SysMenu;
import com.tang.service.admin.model.req.ReqSysMenu;
import com.tang.service.admin.model.req.ReqSysMenuAdd;
import com.tang.service.admin.model.res.ResRoute;
import com.tang.service.admin.model.res.ResSysMenuTableItem;
import com.tang.service.admin.model.res.ResSysMenuDetail;
import com.tang.service.admin.service.ISysMenuService;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.BeanUtils;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import static com.tang.common.utils.excel.ExcelUtils.getStyleStrategy;

/**
 * @author tang jun liang
 * @Date ${gen.dateTime}
 */
@Service
public class SysMenuFacade extends Facade {

    @Resource
    private ISysMenuService sysMenuService;


    /**
     * 菜单表删除
     *
     * @param ids
     * @param currLoginUser
     * @return
     */
    public HttpResult<String> remove(ReqIds ids, LoginUser currLoginUser) {
        sysMenuService.removeBatchByIds(ids.getIds());
        return HttpResult.ok();
    }

    /**
     * 菜单表详情
     *
     * @param id
     * @param currLoginUser
     * @return
     */
    public HttpResult<ResSysMenuDetail> detail(ReqId id, LoginUser currLoginUser) {
        SysMenu sysMenu = sysMenuService.getById(id.getId());
        ResSysMenuDetail resSysMenuDetail = new ResSysMenuDetail();
        BeanUtils.copyProperties(sysMenu, resSysMenuDetail);
        return HttpResult.ok(resSysMenuDetail);
    }

    /**
     * 菜单表列表
     *
     * @param reqSysMenu
     * @param currLoginUser
     * @return
     */
    public HttpResult<List<ResSysMenuTableItem>> list(ReqSysMenu reqSysMenu, LoginUser currLoginUser) {
        List<ResSysMenuTableItem> sysMenuTableItemList = sysMenuService.selectJoinList(
                ResSysMenuTableItem.class,
                new MPJLambdaWrapper<SysMenu>()
                        .selectAll(SysMenu.class)
                        .eq(SysMenu::getMenu, !reqSysMenu.getButton())
                        .eq(Objects.nonNull(reqSysMenu.getId()), SysMenu::getPid, reqSysMenu.getId())
        );
        if (reqSysMenu.getButton()){
            return HttpResult.ok(sysMenuTableItemList);
        }
        Map<Long, List<ResSysMenuTableItem>> sysMenuPidMap = sysMenuTableItemList.stream()
                .collect(Collectors.groupingBy(ResSysMenuTableItem::getPid));
        List<ResSysMenuTableItem> parentMenuList = sysMenuPidMap.getOrDefault(0L, List.of()).stream().
                sorted(Comparator.comparing(ResSysMenuTableItem::getSort)).
                peek(
                        item -> item.setChildren(processChildrenMenu(item.getId(), sysMenuPidMap))
                ).collect(Collectors.toList());
        return HttpResult.ok(parentMenuList);
    }

    private List<ResSysMenuTableItem> processChildrenMenu(Long pid, Map<Long, List<ResSysMenuTableItem>> sysMenuPidMap) {
        List<ResSysMenuTableItem> sysMenuTableItemList = sysMenuPidMap.get(pid);
        if (Objects.isNull(sysMenuTableItemList)) {
            return null;
        }
        sysMenuTableItemList = sysMenuTableItemList.stream().sorted(Comparator.comparing(ResSysMenuTableItem::getSort)).collect(Collectors.toList());
        sysMenuTableItemList.forEach(item -> item.setChildren(processChildrenMenu(item.getId(), sysMenuPidMap)));
        return sysMenuTableItemList;
    }

    /**
     * 新增菜单表
     *
     * @param sysMenuAdd
     * @return
     */
    public HttpResult<String> add(ReqSysMenuAdd sysMenuAdd) {
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(sysMenuAdd, sysMenu);
        sysMenuService.saveOrUpdate(sysMenu);
        return HttpResult.ok();
    }

    /**
     * 更新菜单表
     *
     * @param sysMenuAdd
     * @return
     */
    public HttpResult<String> update(ReqSysMenuAdd sysMenuAdd) {
        return add(sysMenuAdd);
    }


    /**
     * 导出菜单表表
     *
     * @param ids
     * @param httpServletResponse
     */
    @SneakyThrows
    public void export(ReqIds ids, HttpServletResponse httpServletResponse) {
        String fileName = URLEncoder.encode("菜单表.xlsx", StandardCharsets.UTF_8);
        // 设置响应体
        httpServletResponse.setContentType("application/vnd.ms-excel");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + fileName);
        List<SysMenu> sysMenuList = sysMenuService
                .lambdaQuery()
                .in(!ids.getIds().isEmpty(), SysMenu::getId, ids.getIds())
                .list();
        EasyExcel.write(httpServletResponse.getOutputStream(), SysMenu.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(getStyleStrategy())
                .sheet("菜单表").doWrite(sysMenuList);
    }


    /**
     * 导入excel
     *
     * @param file
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public HttpResult<String> importExcel(MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        EasyExcel.read(inputStream, ReqSysMenuAdd.class, new AnalysisEventListener<ReqSysMenuAdd>() {
            final List<SysMenu> sysMenuList = new ArrayList<>();

            @Override
            public void invoke(ReqSysMenuAdd sysMenuAdd, AnalysisContext analysisContext) {
                SysMenu sysMenu = new SysMenu();
                BeanUtils.copyProperties(sysMenuAdd, sysMenu);
                sysMenuList.add(sysMenu);
                if (sysMenuList.size() == Constant.MAX_BATCH) {
                    sysMenuService.saveBatch(sysMenuList);
                    sysMenuList.clear();
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                sysMenuService.saveBatch(sysMenuList);
            }
        });
        return HttpResult.ok();
    }

    /**
     * 导出模版
     *
     * @param httpServletResponse
     */
    @SneakyThrows
    public void exportTemplate(HttpServletResponse httpServletResponse) {
        String fileName = URLEncoder.encode("菜单表导入模版.xlsx", StandardCharsets.UTF_8);
        // 设置响应体
        httpServletResponse.setContentType("application/vnd.ms-excel");
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-disposition", "attachment;filename=" + fileName);
        EasyExcel.write(httpServletResponse.getOutputStream(), SysMenu.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(getStyleStrategy())
                .sheet("菜单表").doWrite(List.of());
    }

    /**
     * 获取菜单路由
     *
     * @param currLoginUser
     * @return
     */
    public HttpResult<List<ResRoute>> getRoute(LoginUser currLoginUser) {
        List<ResSysMenuTableItem> sysMenuTableItemList = sysMenuService.selectJoinList(
                ResSysMenuTableItem.class,
                new MPJLambdaWrapper<SysMenu>()
                        .selectAll(SysMenu.class)
                        .eq(SysMenu::getMenu, true)
        );
        Map<Long, List<ResSysMenuTableItem>> sysMenuPidMap = sysMenuTableItemList.stream()
                .collect(Collectors.groupingBy(ResSysMenuTableItem::getPid));
        List<ResRoute> routes = sysMenuPidMap.getOrDefault(0L, List.of())
                .stream().sorted(Comparator.comparing(ResSysMenuTableItem::getSort)).map(item -> {
                    ResRoute route = new ResRoute();
                    route.setName(item.getMenuName());
                    route.setPath("/" + item.getRoute());
                    route.setRoutes(processRoutes(item, sysMenuPidMap));
                    return route;
                })
                .collect(Collectors.toList());
        return HttpResult.ok(routes);
    }


    /**
     * 处理一级以下的菜单路由
     *
     * @param parent
     * @param sysMenuPidMap
     * @return
     */
    private List<ResRoute> processRoutes(ResSysMenuTableItem parent, Map<Long, List<ResSysMenuTableItem>> sysMenuPidMap) {
        List<ResSysMenuTableItem> resSysMenuTableItemList = sysMenuPidMap
                .getOrDefault(parent.getId(), List.of()).stream()
                .sorted(Comparator.comparing(ResSysMenuTableItem::getSort))
                .collect(Collectors.toList());
        return resSysMenuTableItemList.stream().map(item -> {
            ResRoute route = new ResRoute();
            route.setName(item.getMenuName());
            route.setPath("/" + parent.getRoute() + "/" + item.getRoute());
            route.setAccess("hasAuth");
            route.setRoutes(processRoutes(route, item, item.getId(), sysMenuPidMap));
            return route;
        }).collect(Collectors.toList());
    }


    private List<ResRoute> processRoutes(ResRoute currRoute, ResSysMenuTableItem resSysMenuTableItem, Long pid, Map<Long, List<ResSysMenuTableItem>> sysMenuPidMap) {
        ResRoute route = new ResRoute();
        route.setName(resSysMenuTableItem.getMenuName());
        route.setPath(currRoute.getPath());
        route.setComponent("./" + resSysMenuTableItem.getComponentPath());
        route.setHideInMenu(resSysMenuTableItem.getHideInMenu());
        List<ResSysMenuTableItem> resSysMenuTableItems = sysMenuPidMap
                .getOrDefault(pid, List.of());
        List<ResRoute> routes = new ArrayList<>();
        routes.add(route);
        List<ResRoute> childrenList = resSysMenuTableItems.stream()
                .sorted(Comparator.comparing(ResSysMenuTableItem::getSort))
                .map(item -> {
                    ResRoute resRoute = new ResRoute();
                    resRoute.setName(item.getMenuName());
                    resRoute.setPath(currRoute.getPath() + "/" + item.getRoute());
                    resRoute.setComponent("./" + item.getComponentPath());
                    if (StringUtils.isNotBlank(resRoute.getComponent())) {
                        resRoute.setHideInMenu(true);
                    }
                    return resRoute;
                }).collect(Collectors.toList());
        routes.addAll(childrenList);
        return routes;
    }

    public HttpResult<List<ResSysMenuTableItem>> buttonAuthorizeList(ReqIds ids, LoginUser currLoginUser) {
        if (ids.getIds().isEmpty()){
            return ok();
        }
        List<SysMenu> pageMenuList = sysMenuService.lambdaQuery()
                .isNotNull(SysMenu::getComponentPath)
                .in( SysMenu::getId, ids.getIds())
                .list();
        List<ResSysMenuTableItem> sysMenuTableItemList = pageMenuList
                .stream().map(item ->{
                    ResSysMenuTableItem resSysMenuTableItem = new ResSysMenuTableItem();
                    resSysMenuTableItem.setId(item.getId());
                    resSysMenuTableItem.setMenuName(item.getMenuName());
                    resSysMenuTableItem.setChildren(sysMenuService.selectJoinList(ResSysMenuTableItem.class, new MPJLambdaWrapper<SysMenu>()
                                    .selectAll(SysMenu.class)
                                    .eq(SysMenu::getMenu, false)
                            .eq(SysMenu::getPid, item.getId())
                    ));
                    return resSysMenuTableItem;
                })
                .filter(item -> !item.getChildren().isEmpty()).collect(Collectors.toList());

        return ok(sysMenuTableItemList);
    }



}
